#include "Common.h"
#include "Scene.h"
#include "RenderQueue.h"
#include "Renderable.h"
#include "OctreeScene.h"
#include "OctreeNode.h"
#include "Camera.h"
#include "UpdateObject.h"
#include "PhysicsSystem.h"
#include <algorithm>

//------------------------------------------
Scene::Scene(SceneManager* creator, float halfWorldSize)
	: mCreator(creator)
	, mRenderQueue(NULL)
	, mOctree(NULL)
	, mPhysicsScene(NULL)
{
	mRenderQueue = new RenderQueue();
	mOctree = new SceneOctree(Vector3::Zero, halfWorldSize);
	mPhysicsScene = Physics::System::Instance().CreateScene();
}
//------------------------------------------
Scene::~Scene()
{
	ClearScene();

	if (mPhysicsScene) Physics::System::Instance().DestroyScene(mPhysicsScene);
	if (mOctree) delete mOctree;
	if (mRenderQueue) delete mRenderQueue;

	removeAllCamera();
}
//------------------------------------------
SceneNode* Scene::createSceneNode(const std::string& name, const Vector3& pos, const Rotator& rotate, const Vector3& scale)
{
	if (hasSceneNode(name))
		throw std::runtime_error("the SceneNode name: " + name + " already exist!!!");

	OctreeNode* node = new OctreeNode(name, this);
	node->setPosition(pos);
	node->setRotation(rotate);
	node->setScale(scale);

	mSceneNodeList[name] = node;
	return node;
}
//------------------------------------------
SceneNode* Scene::getSceneNode(const std::string& name)
{
	SceneNodeList::iterator it = mSceneNodeList.find(name);
	if (it != mSceneNodeList.end())
		return it->second;
	return NULL;
}
//------------------------------------------
bool Scene::hasSceneNode(const std::string& name) const
{
	return mSceneNodeList.find(name) != mSceneNodeList.end();
}
//------------------------------------------
void Scene::removeSceneNode(const std::string& name)
{
	SceneNodeList::iterator it = mSceneNodeList.find(name);
	if (it != mSceneNodeList.end())
	{
		delete it->second;
		mSceneNodeList.erase(it);
	}
}
//------------------------------------------
void Scene::removeAllSceneNode()
{
	for (SceneNodeList::iterator it(mSceneNodeList.begin()), end(mSceneNodeList.end()); it != end; it++)
		delete it->second;
	mSceneNodeList.clear();
}
//------------------------------------------
Camera* Scene::createCamera(const std::string& name)
{
	if (hasCamera(name))
		throw std::runtime_error("the camera name: " + name + " already exist!!!");

	Camera* camera = ALIGN_NEW(Camera, (name, this));

	mCameraList[name] = camera;
	return camera;
}
//------------------------------------------
Camera* Scene::getCamera(const std::string& name)
{
	CameraList::iterator it = mCameraList.find(name);
	if (it != mCameraList.end())
		return it->second;
	return NULL;
}
//------------------------------------------
bool Scene::hasCamera(const std::string& name) const
{
	return mCameraList.find(name) != mCameraList.end();
}
//------------------------------------------
void Scene::removeCamera(const std::string& name)
{
	CameraList::iterator it = mCameraList.find(name);
	if (it != mCameraList.end())
	{
		ALIGN_DELETE(Camera, it->second);
		mCameraList.erase(it);
	}
}
//------------------------------------------
void Scene::removeAllCamera()
{
	for (CameraList::iterator it(mCameraList.begin()), end(mCameraList.end()); it != end; it++)
		ALIGN_DELETE(Camera, it->second);
	mCameraList.clear();
}
//------------------------------------------
Scene::MovableObjectCollection* Scene::getMovableObjectCollection(const std::string& typeName)
{
	MovableObjectCollectionMap::iterator it = mMovableObjectCollectionMap.find(typeName);
	if (it != mMovableObjectCollectionMap.end())
		return it->second;
	
	MovableObjectCollection* newCollection = new MovableObjectCollection();
	mMovableObjectCollectionMap[typeName] = newCollection;
	return newCollection;
}
//------------------------------------------
const Scene::MovableObjectCollection* Scene::getMovableObjectCollection(const std::string& typeName) const
{
	MovableObjectCollectionMap::const_iterator it = mMovableObjectCollectionMap.find(typeName);
	if (it != mMovableObjectCollectionMap.end())
		return it->second;
	return NULL;
}
//------------------------------------------
bool Scene::AddMovableObject(MovableObject* object)
{
	MovableObjectCollection* collection = getMovableObjectCollection(object->getMovableType());
	if (collection == NULL)
		return false;

	MovableObjectCollection::const_iterator it = collection->find(object->getName());
	if (it != collection->end())
		return false;

	(*collection)[object->getName()] = object;
	return true;
}
//------------------------------------------
MovableObject* Scene::FindMovableObject(const std::string& name, const std::string& typeName) const
{
	const MovableObjectCollection* collection = getMovableObjectCollection(typeName);
	if (collection == NULL)
		return NULL;

	MovableObjectCollection::const_iterator it = collection->find(name);
	if (it == collection->end())
		return NULL;

	return it->second;
}
//------------------------------------------
bool Scene::Destroy(const std::string& name, const std::string& typeName)
{
	MovableObjectCollection* collection = getMovableObjectCollection(typeName);
	if (collection == NULL)
		return false;

	MovableObjectCollection::iterator it = collection->find(name);
	if (it == collection->end())
		return false;

	ALIGN_DELETE(MovableObject, it->second);
	collection->erase(it);

	return true;
}
//------------------------------------------
bool Scene::Destroy(MovableObject* object)
{
	return Destroy(object->getName(), object->getMovableType());
}
//------------------------------------------
void Scene::DestroyAllMovable()
{
	for (MovableObjectCollectionMap::iterator collectIt(mMovableObjectCollectionMap.begin()), collectEnd(mMovableObjectCollectionMap.end()); collectIt != collectEnd; collectIt++)
	{
		MovableObjectCollection* collection = collectIt->second;
		for (MovableObjectCollection::iterator it(collection->begin()), end(collection->end()); it != end; it++)
			ALIGN_DELETE(MovableObject, it->second);
		delete collection;
	}
	mMovableObjectCollectionMap.clear();
}
//------------------------------------------
void Scene::ClearScene()
{
	DestroyAllMovable();

	removeAllSceneNode();

	mRenderableList.clear();
}
//------------------------------------------
void Scene::PendingUpdate(UpdateObject* object)
{
	mPendingUpdates.push_back(object);
}
//------------------------------------------
void Scene::RemovePendingUpdate(UpdateObject* object)
{
	UpdateObjectVector::iterator it = std::find(mPendingUpdates.begin(), mPendingUpdates.end(), object);
	if (it != mPendingUpdates.end())
	{
		if (*it != mPendingUpdates.back())
			*it = mPendingUpdates.back();
		mPendingUpdates.pop_back();
	}
}
//------------------------------------------
void Scene::CreateUpdater(UpdateObject* object)
{
	mUpdateList.push_back(object);
}
//------------------------------------------
void Scene::RemoveUpdater(size_t ticket, UpdateObject* object)
{
	UpdateObjectVector::iterator it = std::find(mUpdateList.begin(), mUpdateList.end(), object);
	if (it != mUpdateList.end())
	{
		if (*it != mUpdateList.back())
			*it = mUpdateList.back();
		mUpdateList.pop_back();
	}
}
//------------------------------------------
void Scene::Update(float elapsedTime)
{
	if (!mPendingUpdates.empty())
	{
		for (UpdateObjectVector::iterator it(mPendingUpdates.begin()), end(mPendingUpdates.end()); it != end; it++)
			(*it)->Update(elapsedTime);
		mPendingUpdates.clear();
	}

	for (UpdateObjectVector::iterator it(mUpdateList.begin()), end(mUpdateList.end()); it != end; it++)
		(*it)->Update(elapsedTime);
}
//------------------------------------------
void Scene::AddToScene(Renderable* rend)
{
	// allocate a new scene ID.
	rend->setSceneID(mRenderableList.size());
	mRenderableList.push_back(rend);
}
//------------------------------------------
void Scene::RemoveFromScene(Renderable* rend)
{
	if (mRenderableList.empty())
		return;

	size_t sceneID = rend->getSceneID();
	assert(mRenderableList[sceneID] == rend && "the render id isn't match.");

	// swap delete if it not at the end.
	if (sceneID + 1 < mRenderableList.size())
	{
		// reset the scene id
		Renderable* tail = mRenderableList.back();
		mRenderableList[sceneID] = tail;

		tail->UpdateSceneID(sceneID);
	}

	mRenderableList.resize(mRenderableList.size() - 1);
}
//------------------------------------------
float Scene::RayCast(const Vector3& pos, const Vector3& dir, float maxDistance)
{
	return Physics::System::Instance().RayCast(
		mPhysicsScene,
		pos, dir,
		maxDistance);
}
//------------------------------------------
